package com.study.mike.common.config;

import cn.hutool.json.JSONUtil;
import com.study.mike.common.annotation.MyLog;
import com.study.mike.model.entity.SysOperLogDO;
import com.study.mike.service.SysOperLogService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Date;

/**
 * 记录系统操作日志aop
 *
 * @author KangHui
 * @Date 2022/9/26 14:02
 */
@Aspect
@Component
@Slf4j
public class LogAspect {
    @Resource
    private SysOperLogService sysOperLogService;

    @Pointcut("@annotation(com.study.mike.common.annotation.MyLog)")
    public void logPointCut() {
        log.info("切点");
    }

    @AfterReturning(pointcut = "logPointCut()")
    public void doAfterReturning(JoinPoint joinPoint) {
        handleLog(joinPoint, null);

    }

    @AfterThrowing(value = "logPointCut()", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Exception e) {
        handleLog(joinPoint, e);
    }

    private void handleLog(final JoinPoint joinPoint, final Exception e) {
        // 获得MyLog注解
        MyLog controllerLog = getAnnotationLog(joinPoint);
        if (controllerLog == null) {
            return;
        }
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        // 通过请求头获取用户code
        String userCode = request.getHeader("operation");
        // 获取请求appSecret
        SysOperLogDO operLog = new SysOperLogDO();
        operLog.setOperation(userCode);

        // 操作状态（0正常 1异常）
        operLog.setStatus(0);
        // 操作时间
        operLog.setOperationTime(new Date());
        if (e != null) {
            operLog.setStatus(1);
            operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000));
        }

        // 处理注解上的参数
        getControllerMethodDescription(joinPoint, controllerLog, operLog);
        // 保存数据库
        sysOperLogService.save(operLog);
    }

    /**
     * 获取Controller层上MyLog注解中对方法的描述信息
     *
     * @param joinPoint 切点
     * @param myLog     自定义的注解
     * @param operLog   操作日志实体类
     */
    private void getControllerMethodDescription(JoinPoint joinPoint, MyLog myLog, SysOperLogDO operLog) {
        // 设置业务类型（0其它 1新增 2修改 3删除）
        operLog.setMethodType(myLog.methodType().ordinal());
        // 设置模块标题，eg:登录
        operLog.setTitle(myLog.title());
        // 对方法上的参数进行处理，处理完：userName=xxx,password=xxx
        /*获取参数*/
        Object[] params = joinPoint.getArgs();
        Object[] copyParam = new Object[params.length];
        if (params.length > 0) {
            for (int i = 0; i < params.length; i++) {
                if (params[i] instanceof ServletRequest ||
                        params[i] instanceof ServletResponse ||
                        params[i] instanceof MultipartFile) {
                    continue;
                }
                copyParam[i] = params[i];
            }
        }
        String paramsStr;
        if (copyParam.length > 0 && copyParam.length == 1) {
            paramsStr = JSONUtil.toJsonStr(copyParam[0]);
        } else if (copyParam.length > 1) {
            paramsStr = JSONUtil.toJsonStr(copyParam);
        } else {
            paramsStr = "";
        }
        operLog.setMethodName(StringEscapeUtils.unescapeJava(paramsStr));
    }

    /**
     * 是否存在注解，如果存在就获取，不存在则返回null
     *
     * @param joinPoint
     * @return
     */
    private MyLog getAnnotationLog(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        if (method != null) {
            return method.getAnnotation(MyLog.class);
        }
        return null;
    }
}
